﻿using System.Collections.Generic;
using System.Linq;
using Medianamik.Core.DAL.Repositories;
using Medianamik.Core.Interfaces;

namespace Medianamik.Core.DAL
{
    public interface INodeMapper
    {
        INodeWrapper MapNode(IMedianamikDTO dto, MappingOptions options);
        INodeWrapper MapNode(IMedianamikDTO dto);
    }

    public class NodeMapper : INodeMapper
    {
        private readonly INodeRepository _nodeRepository;
        private readonly INodeTypeRepository _nodeTypeRepository;

        public NodeMapper(INodeRepository nodeRepository,
                          INodeTypeRepository nodeTypeRepository)
        {
            _nodeRepository = nodeRepository;
            _nodeTypeRepository = nodeTypeRepository;
        }

        public INodeWrapper MapNode(IMedianamikDTO dto)
        {
            return MapNode(dto, MappingOptions.DefaultOptions);
        }

        public INodeWrapper MapNode(IMedianamikDTO dto, MappingOptions options)
        {
            var nodeToSave = GetNodeToSave(dto, options.ValidateHierarchicalRules);

            //Ne pas faire nodeToSave.NodeType --> Ne respecte pas le Repository Pattern
            //TODO: S'assurer que NodeType.Properties n'est pas lazy loaded

            MapProperties(dto, _nodeTypeRepository.GetById(dto.TypeId), nodeToSave);

            nodeToSave.InnerNode.Position = dto.Position;

            return nodeToSave;
        }

        protected virtual INodeWrapper GetNodeToSave(IMedianamikDTO dto, bool validateHierarchicalRules)
        {
            //TODO: Pourrait être bien...
            //if(dto.TypeId == Guid.Empty)
            //{
            //    var field = dto.GetType()
            //        .GetField("TYPE_ID", BindingFlags.Public | BindingFlags.Static);

            //    dto.TypeId = (Guid)field.GetValue(null);

            //}

            return _nodeRepository.GetExistingNode(dto) ??
                _nodeRepository.NewNode(dto, validateHierarchicalRules);
        }

        protected virtual void MapProperties(IMedianamikDTO dto, NodeType nodeType,
            INodeWrapper nodeToSave)
        {
            foreach (var propertyName in GetNodeTypePropertyNames(nodeType))
            {
                var propertyInfo = dto.GetType().GetProperty(propertyName);

                if (propertyInfo != null)
                {
                    if (dto.CultureName.Length != 0 && !nodeToSave.InnerNode.IsLanguageNeutral &&
                        !nodeToSave.InnerNode.Instances.Exists(dto.CultureName))
                    {
                        nodeToSave.InnerNode.AddNodeInstance(dto.CultureName);
                    }

                    nodeToSave.InnerNode[dto.CultureName][propertyName]
                        .SetValue(propertyInfo.GetValue(dto, null));
                }
            }
        }

        protected virtual IEnumerable<string> GetNodeTypePropertyNames(NodeType nodeType)
        {
            return nodeType.GetAllProperties().Select(p => p.Name);
        }
    }

    public class MappingOptions
    {
        private bool _validateHierarchicalRules = true;
        public bool ValidateHierarchicalRules
        {
            get { return _validateHierarchicalRules; }
            set { _validateHierarchicalRules = value; }
        }

        public static MappingOptions DefaultOptions { get { return new MappingOptions(); } }
    }
}
